---
title: 服务器群岛
description: 将高性能静态 HTML 与服务器渲染的动态内容相结合。
i18nReady: true
---

服务器群岛允许你单独按需渲染动态或个性化的 "群岛"，且无需牺牲页面其他部分的性能。

这意味着你的访客将更快地看到你页面最重要的部分，并允许你的主要内容被更积极地缓存，从而提供更快的性能。

## 服务器群岛组件

服务器孤岛是一个普通的服务器渲染的 [Astro 组件](/zh-cn/basics/astro-components/)，它被指定为延迟渲染，直到其内容可用。

你的页面将立即使用任何指定的 [回退内容作为占位](#服务器群岛回退内容) 来渲染。然后，在客户端上获取组件自己的内容，并在可用时显示。

安装 [适配器](/zh-cn/guides/on-demand-rendering/#服务器适配器) 来执行延迟渲染后，将 ['server：defer' 指令](/zh-cn/reference/directives-reference/#服务器端指令) 添加到页面上的任意组件，来将其变成组件自己的岛屿：

```astro title="src/pages/index.astro" "server:defer"
---
import Avatar from '../components/Avatar.astro';
---
<Avatar server:defer />
```

这些组件可以使用适配器执行 [你在按需渲染页面中通常执行的任何操作](/zh-cn/guides/on-demand-rendering/#按需渲染特性)，例如获取内容和访问 cookie：

```astro title="src/components/Avatar.astro"
---
import { getUserAvatar } from '../sessions';
const userSession = Astro.cookies.get('session');
const avatarURL = await getUserAvatar(userSession);
---
<img alt="User avatar" src={avatarURL} />
```

### 向服务器群岛组件传递 Props

传递给服务器群岛组件的属性必须 [序列化](https://developer.mozilla.org/en-US/docs/Glossary/Serialization)：转换为适合在网络传输或存储的格式。然而，Astro 并不能序列化所有类型的数据结构。因此，在将属性传递给服务器群岛组件时，存在一些限制。

以下是支持的属性类型：普通对象、`number`、`string`、`Array`、`Map`、`Set`、`RegExp`、`Date`、`BigInt`、`URL`、`Uint8Array`、`Uint16Array`、`Uint32Array` 和  `Infinity`

需要注意的是，函数不能传递给带有 `server:defer` 标记的组件，因为它们不能被序列化。具有循环引用的对象也不能被序列化。

## 服务器群岛回退内容

当在组件上使用 `server：defer` 属性来延迟其渲染时，你可以在默认的载入内容中插入包含 `"fallback"` 的 "slot"。

你的回退内容最初将在页面加载时与页面的其余部分一起渲染，并在可用时替换为组件的内容。

要添加回退内容，请在传递给服务器岛屿组件的子组件（其他组件或 HTML 元素）上添加 `slot="fallback"`：

```astro
---
import Avatar from '../components/Avatar.astro';
import GenericAvatar from '../components/GenericAvatar.astro';
---
<Avatar server:defer>
  <GenericAvatar slot="fallback" />
</Avatar>
```

此回退内容可以是：

- 一个通用头像，用以代替用户自己的头像。
- 占位符 UI，例如自定义消息。
- 加载进度提示，例如旋转加载样式。


## 工作原理

服务器孤岛的实现主要发生在构建时，那时组件内容会被转换成一个小脚本。

每个被标记上 `server：defer` 的孤岛都被拆分为自己的特殊路由，脚本在运行时会获取该路由。当 Astro 构建你的网站时，它会略过该组件并在其位置注入一个脚本，以及你用 `slot="fallback"` 标记的任何内容。

当页面在浏览器中加载时，这些组件将被请求到一个特殊端点，该端点渲染这些组件并返回 HTML。这意味着用户将立即看到页面最关键的部分。而在加载动态群岛之前，回退内容将在短时间内可见。

每个岛屿都独立于其他岛屿进行加载。这意味着较慢的岛屿也不会拖累你其余个性化内容的加载。

此渲染模式旨在实现可移植性。它不依赖于任何服务器基础设施，因此它可以与你拥有的任何主机配合使用，从 Docker 容器中的 Node.js 服务器到你选择的无服务器提供商。

## 缓存

服务器群岛的数据是通过 `GET` 请求检索的，在 URL 查询中将 props 作为加密字符串传递。这允许使用标准的 `Cache-Control` 指令通过 [`Cache-Control` HTTP 标头](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control) 来缓存数据。

但是，出于实际原因，[浏览器将 URL 限制为最大长度 2048 字节](https://chromium.googlesource.com/chromium/src/+/master/docs/security/url_display_guidelines/url_display_guidelines.md#url-length)，以避免导致服务被拒绝的问题。如果你的查询字符串导致你的 URL 超过这个限制，Astro 将发送一个 `POST` 请求，其中包含正文中的所有 props。

浏览器不会缓存 `POST` 请求，因为它们是用于提交数据的，如果缓存则可能会导致数据完整性或安全问题。因此，项目中的任何现有缓存逻辑都将中断，尽可能只将必要的 props 传递给你的服务器群岛，而避免发送整个数据对象和数组，以保持你的查询字符串足够小。

## 访问服务器岛屿中的页面 URL

在大多数情况下，你的服务器岛屿组件可以通过 [传递 props](/zh-cn/basics/astro-components/#组件参数) 来获取有关渲染它的页面的信息，就像在普通组件中一样。

但是，服务器群岛是在页面请求之外的、自己的隔离上下文中运行的。服务器岛屿组件中的 `Astro.url` 和 `Astro.request.url` 都会返回一个结构看起来像 `/_server-islands/Avatar` 的 URL，而不是浏览器中当前页面的 URL。此外，如果你正在预渲染页面，你将无法访问 query 参数等信息，以便其作为 props 传递。

要从页面的 URL 访问信息，你可以检查 [Referer](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referer) 标头，其中将会包含：在浏览器中加载该岛屿的页面的地址：

```astro
---
const referer = Astro.request.headers.get('Referer');
const url = new URL(referer);
const productId = url.searchParams.get('product');
---
```

## 复用加密密钥

Astro 使用 [加密技术](https://developer.mozilla.org/zh-CN/docs/Glossary/Cryptography) 来加密传入到服务器群岛的 prop，防止敏感数据意外泄露。该加密机制依赖于每次构建时生成的随机密钥，该密钥会嵌入到服务端包中。

大多数部署托管平台会自动保持前端与后端的同步。但在滚动部署、多区域托管、缓存含服务端岛屿页面的 CDN 这些场景中，你可能需要使用固定的加密密钥。

在滚动部署（例如：Kubernetes）的环境中，前端资源（负责加密 prop）和后端函数（负责解密 prop）可能会临时使用不同的密钥，又或者是 CDN 可能仍在分发用旧密钥构建的页面，导致传递给服务端岛屿的加密 prop 无法被解密。

在这些情况下，使用 Astro CLI 生成可复用的加密密钥，并在构建环境中，设置你的环境变量：

```shell
astro create-key
```

将该值配置到 `ASTRO_KEY` 环境变量中（如在 `.env` 文件中）并包含在 CI/CD 或主机的构建设置中。这能确保构建时始终使用同一密钥，保持加密与解密同步。
